package org.videopractice.network;

import org.videopractice.common.Logger;
import org.videopractice.common.StringUtils;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import io.netty.channel.Channel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

public class ChannelManager {

    public static final AttributeKey<IoSession> SESSION_KEY = AttributeKey.valueOf("session");

    /** 缓存登录账户与服务器会话的映射的session */
    private static final ConcurrentMap<String, IoSession> account2SessionMap = new ConcurrentHashMap<>();

    /** 缓存服务器会话与登录账户的映射 */
    // ConcurrentHashMap 在线程安全的基础上提供了更好的写并发能力，但同时降低了对读一致性的要求
    private static final Map<IoSession, String> session2AccountMap = new ConcurrentHashMap<>();

    /**
     * 给通道绑定添加新的会话
     * @param channel 目标通道
     * @param session 会话
     * @return true 则绑定成功，false 则失败，如果已存在会话则绑定失败
     */
    public static boolean addSession(Channel channel, IoSession session) {
        Attribute<IoSession> sessionAttr = channel.attr(SESSION_KEY);
        return sessionAttr.compareAndSet(null, session);
    }

    /**
     * 根据通道获取绑定会话
     * @param channel 目标通道
     * @return IoSession 会话
     */
    public static IoSession getSession(Channel channel) {
        Attribute<IoSession> sessionAttr = channel.attr(SESSION_KEY);
        return sessionAttr.get();
    }

    /**
     * 注册会话
     * @param account 账户
     * @param channel 通道
     * @return
     */
    public static IoSession registerSession(String account, Channel channel) {
        try {
            IoSession session = getSession(channel);

            session.setAccount(account);
            session.setReconnected(session.isLogin() ? Boolean.TRUE : Boolean.FALSE);
//            session.setLogin(Boolean.TRUE);
//            session.setClientType(clientType);

            // 登录账号只能单点登录，账号在其他客户端登录时，则退出原客户端
            IoSession oriSession = account2SessionMap.get(account);
            if(oriSession != null) {
                if(oriSession.channel == null || !channel.id().equals(oriSession.channel.id())) {
                    // 不同链接,重复登录
                    Logger.w(String.format("不同链接重复登录:%s, 新链接:%s, 原链接:%s", account, channel, oriSession.channel));
                    session2AccountMap.remove(oriSession);
                    oriSession.close();	// 登出：异地登录
                } else {
                    // 同一链接,重复登录
                    Logger.w(String.format("同一链接重复登录:%s, 地址:%s, 通道ID:%s", account, session.ipAddress, channel.id()));
                }
            }

            session2AccountMap.put(session, account);
            account2SessionMap.put(account, session);
            return session;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 注销会话
     * @param channel 通道
     * @return
     */
    public static IoSession unregisterContext(Channel channel) {
        if (channel == null) {
            return null;
        }

        IoSession session = getSession(channel);
        String accountNo = session2AccountMap.remove(session); // 删除会话
        if (StringUtils.isNotEmpty(accountNo)) {
            account2SessionMap.remove(accountNo);
        }

        if (session != null) {
            session.close();
        }
        return session;
    }

    /**
     * 根据登录账户获取会话
     * @param accountNo
     * @return
     */
    public static IoSession getSession(String accountNo) {
        return account2SessionMap.get(accountNo);
    }

    /**
     * 判断会话是否存在
     * @param channel
     * @return
     */
    public static boolean validateSession(Channel channel) {
        IoSession session = getSession(channel);
        if (session == null) {
            return Boolean.FALSE;
        }
        return session.isLogin();
    }

    /**
     * 返回服务器当前总链接数
     * @return
     */
    public static int getConnectCount() {
        return session2AccountMap.size();
    }

    public static Collection<IoSession> allSession() {
        return account2SessionMap.values();
    }
} 
